﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Xml;
using System.Xml.Linq;
using System.Web.Compilation;
using System.Collections.Specialized;
using BrandonHaynes.Membership.Factors;

namespace BrandonHaynes.Membership
	{
	/// <summary>
	/// A configuration handler for the brandonHaynes/authenticationFactors node of the web.config
	/// </summary>
	public class FactorConfigurationHandler : IConfigurationSectionHandler
		{
		#region IConfigurationSectionHandler Members

		public object Create(object parent, object configContext, XmlNode section)
			{
			// Create a dictionary of factor names to IAuthenticationFactor instances
			return section
				.ChildNodes.Cast<XmlNode>()
				.Select(node => CreateFactor(node))
				.ToDictionary(factor => factor.Name);
			}

		#endregion

		private static IAuthenticationFactor CreateFactor(XmlNode factorNode)
			{
			if (factorNode == null)
				throw new ArgumentNullException("factorNode");

			// Use reflection to create our factors; note that in addition to implementing the IAuthenticationFactor
			// interface, an authentication factor must have a constructor with prototype 
			// this(Dictionary<string, string>).
			//		Would support for default constructors be useful here?  Maybe.
			var factorType = BuildManager.GetType(factorNode.Attributes["type"].Value, true, true);
			return (IAuthenticationFactor)factorType.InvokeMember(string.Empty, System.Reflection.BindingFlags.CreateInstance, null, null, new object[] { CreateAttributeMap(factorNode.Attributes) }, null);
			}

		private static IDictionary<string, string> CreateAttributeMap(XmlAttributeCollection attributes)
			{
			// Map our collection to a dictionary, for later conciseness
			return attributes.Cast<XmlAttribute>().ToDictionary(
				attribute => attribute.Name, 
				attribute => attribute.Value);
			}
		}
	}
